/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2016年7月12日
 * V4.0
 */
package com.jphenix.share.util;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;

import com.jphenix.standard.docs.ClassInfo;

/**
   * 字节数组处理工具
 * 
 * 2018-11-28 增加了一些针对字节数组的处理方法
 * 2019-04-11 增加了替换一批字节数组方法，并修改了替换方法
 * 
 * @author MBG
 * 2016年7月12日
 */
@ClassInfo({"2019-04-11 13:07","字节数组处理工具"})
public class BytesArrayUtil {
	
	
	/**
	* KMP算法类
	* <p/>
	* Created on 2011-1-3
	*/
	static class KMPMatcher {

		private int[] failure;
		private int matchPoint;
		private byte[] bytePattern;

		/**
		 * Method indexOf …
		 *
		 * @param text of type byte[]
		 * @param startIndex of type int
		 * @return int
		 */
		public int indexOf(byte[] text, int startIndex) {
			int j = 0;
			if (text.length == 0 || startIndex > text.length) {
				return -1;
			}
			for (int i = startIndex; i < text.length; i++) {
				while (j > 0 && bytePattern[j] != text[i]) {
					j = failure[j - 1];
				}
				if (bytePattern[j] == text[i]) {
					j++;
				}
				if (j == bytePattern.length) {
					matchPoint = i-bytePattern.length + 1;
					return matchPoint;
				}
			}
			return -1;
		}

	 

		/**
		 * 找到末尾后重头开始找
		 *
		 * @param text of type byte[]
		 * @param startIndex of type int
		 * @return int
		 */

		public int lastIndexOf(byte[] text, int startIndex) {
			matchPoint = -1;
			int j = 0;
			if (text.length == 0 || startIndex > text.length) {
				return -1;
			}
			int end = text.length;
			for (int i = startIndex; i < end; i++) {
				while (j > 0 && bytePattern[j] != text[i]) {
					j = failure[j - 1];
				}
				if (bytePattern[j] == text[i]) {
					j++;
				}
				if (j == bytePattern.length) {
					matchPoint = i-bytePattern.length + 1;
					if ((text.length-i) > bytePattern.length) {
						j = 0;
						continue;
					}
					return matchPoint;
				}
				//如果从中间某个位置找，找到末尾没找到后，再重头开始找
				if (startIndex != 0 && i + 1 == end) {
					end = startIndex;
					i = -1;
					startIndex = 0;
				}
			}
			return matchPoint;
		}

	 

	 

		/**
		 * 找到末尾后不会重头开始找
		 *
		 * @param text of type byte[]
		 * @param startIndex of type int
		 * @return int
		 */
		public int lastIndexOfWithNoLoop(byte[] text, int startIndex) {
			matchPoint = -1;
			int j = 0;
			if (text.length == 0 || startIndex > text.length) {
				return -1;
			}
			for (int i = startIndex; i < text.length; i++) {
				while (j > 0 && bytePattern[j] != text[i]) {
					j = failure[j - 1];
				}
				if (bytePattern[j] == text[i]) {
					j++;
				}
				if (j == bytePattern.length) {
					matchPoint = i-bytePattern.length + 1;
					if ((text.length-i) > bytePattern.length) {
						j = 0;
						continue;
					}
					return matchPoint;
				}
			}
			return matchPoint;
		}

	 

		/**
		 * Method computeFailure4Byte …
		 *
		 * @param patternStr of type byte[]
		 */
		public void computeFailure4Byte(byte[] patternStr) {
			bytePattern = patternStr;
			int j = 0;
			int len = bytePattern.length;
			failure = new int[len];
			for (int i = 1; i < len; i++) {
				while (j > 0 && bytePattern[j] != bytePattern[i]) {
					j = failure[j - 1];
				}
				if (bytePattern[j] == bytePattern[i]) {
					j++;
				}
				failure[i] = j;
			}
		}
	}
	

	/**
	* 查找并替换指定byte数组
	*
	* @param org of type byte[] 原数组
	* @param search of type byte[] 要查找的数组
	* @param replace of type byte[] 要替换的数组
	* @return byte[] 返回新的数组
	* @throws UnsupportedEncodingException when
	*/
	public static byte[] arrayReplace(
			byte[] org,byte[] search,byte[] replace) throws Exception {
		if(org==null) {
			org = new byte[0];
		}
		if(search==null || replace==null) {
			return org;
		}
		int    startIndex = 0; //起始索引
		int    newLength;      //处理后的长度
		byte[] newBytes;       //处理后的字节数组
		//获取是否存在搜索关键字位置
		int index = indexOf(org, search, startIndex);
		while(index != -1) {
			newLength = org.length+replace.length-search.length;
			newBytes  = new byte[newLength];
			System.arraycopy(org, 0, newBytes, 0, index);
			System.arraycopy(replace, 0, newBytes, index, replace.length);
			System.arraycopy(org, index + search.length, newBytes, index + replace.length,(org.length-index-search.length));
			org        = newBytes;
			startIndex = index + replace.length;
			index      = indexOf(org, search, startIndex);
		}
		return org;
	}
	
	
	/**
	* 查找并替换指定byte数组（多个）
	*
	* @param org of type byte[] 原数组
	* @param search of type byte[] 要查找的数组（多个）
	* @param replace of type byte[] 要替换的数组（多个）
	* @param startIndex of type int 开始搜索索引
	* @return byte[] 返回新的数组
	* @throws UnsupportedEncodingException when
	*/
	public static byte[] arrayReplace(
			byte[] org,byte[][] search,byte[][] replace) throws Exception {
		if(org==null) {
			org = new byte[0];
		}
		if(search==null || replace==null) {
			return org;
		}
		int    startIndex;     //从首位处理
		int    index;          //位置
		int    newLength;      //新的长度
		byte[] newBytes;       //处理后的字节数组
		for(int i=0;i<search.length;i++) {
			startIndex = 0;
			index      = indexOf(org, search[i], startIndex);
			while (index != -1) {
				newLength = org.length+replace[i].length-search[i].length;
				newBytes  = new byte[newLength];
				System.arraycopy(org, 0, newBytes, 0, index);
				System.arraycopy(replace[i], 0, newBytes, index, replace[i].length);
				System.arraycopy(org, index + search[i].length, newBytes, index + replace[i].length,(org.length-index-search[i].length));
				org        = newBytes;
				startIndex = index + replace[i].length;
				index      = indexOf(org, search[i], startIndex);
			}
		}
		return org;
	}

	 

	/**
	* 从指定数组的copy一个子数组并返回
	*
	* @param org of type byte[] 原数组
	* @param to 合并一个byte[]
	* @return 合并的数据
	*/
	public static byte[] append(byte[] org, byte[] to) {
		if(org==null) {
			org = new byte[0];
		}
		if(to==null) {
			to = new byte[0];
		}
		byte[] newByte = new byte[org.length + to.length];
		System.arraycopy(org, 0, newByte, 0, org.length);
		System.arraycopy(to, 0, newByte, org.length, to.length);
		return newByte;
	}

	 

	/**
	* 从指定数组的copy一个子数组并返回
	*
	* @param org of type byte[] 原数组
	* @param to 合并一个byte
	* @return 合并的数据
	*/
	public static byte[] append(byte[] org, byte to) {
		if(org==null) {
			org = new byte[0];
		}
		byte[] newByte = new byte[org.length + 1];
		System.arraycopy(org, 0, newByte, 0, org.length);
		newByte[org.length] = to;
		return newByte;
	}

	 

	/**
	* 从指定数组的copy一个子数组并返回
	*
	* @param org of type byte[] 原数组
	* @param from 起始点
	* @param append 要合并的数据
	*/
	public static void append(byte[] org, int from, byte[] append) {
		if(org==null) {
			org = new byte[0];
		}
		if(append==null) {
			append = new byte[0];
		}
		System.arraycopy(append, 0, org, from, append.length);
	}

	 

	/**
	* 从指定数组的copy一个子数组并返回
	*
	* @param original of type byte[] 原数组
	* @param from 起始点
	* @param to 结束点
	* @return 返回copy的数组
	*/
	public static byte[] copyOfRange(byte[] original, int from, int to) throws Exception {
		if(original==null) {
			original = new byte[0];
		}
		int newLength = to-from;
		if (newLength < 0) {
			throw new Exception(from+" > "+to);
		}
		byte[] copy = new byte[newLength];
		System.arraycopy(original, from, copy, 0,Math.min(original.length-from,newLength));
		return copy;
	}

	 
	/**
	 * 字符转字节
	 * @param encode  编码格式
	 * @param chars   待转换 字符数组
	 * @return        转换后的字节数组
	 * 2018年11月28日
	 * @author MBG
	 */
	public static byte[] char2byte(String encode, char[] chars) {
		if(chars==null) {
			chars = new char[0];
		}
		if(encode==null) {
			encode = "UTF-8";
		}
		Charset cs = Charset.forName(encode);
		CharBuffer cb = CharBuffer.allocate(chars.length);
		cb.put(chars);
		cb.flip();
		ByteBuffer bb = cs.encode(cb);
		return bb.array();
	}

	 

	/**
	* 查找指定数组的起始索引
	*
	* @param org of type byte[] 原数组
	* @param search of type byte[] 要查找的数组
	* @return int 返回索引
	*/
	public static int indexOf(byte[] org, byte[] search) {
		if(org==null) {
			org = new byte[0];
		}
		if(search==null) {
			search = new byte[0];
		}
		return indexOf(org, search, 0);
	}

	 

	/**
	* 查找指定数组的起始索引
	*
	* @param org of type byte[] 原数组
	* @param search of type byte[] 要查找的数组
	* @param startIndex 起始索引
	* @return int 返回索引
	*/
	public static int indexOf(byte[] org, byte[] search, int startIndex) {
		if(org==null) {
			org = new byte[0];
		}
		if(search==null) {
			search = new byte[0];
		}
		KMPMatcher kmpMatcher = new KMPMatcher();
		kmpMatcher.computeFailure4Byte(search);
		return kmpMatcher.indexOf(org, startIndex);
	}

	 

	/**
	* 查找指定数组的最后一次出现起始索引
	*
	* @param org of type byte[] 原数组
	* @param search of type byte[] 要查找的数组
	* @return int 返回索引
	*/

	public static int lastIndexOf(byte[] org, byte[] search) {
		return lastIndexOf(org, search, 0);
	}

	 

	/**
	* 查找指定数组的最后一次出现起始索引
	*
	* @param org of type byte[] 原数组
	* @param search of type byte[] 要查找的数组
	* @param fromIndex 起始索引
	* @return int 返回索引
	*/
	public static int lastIndexOf(byte[] org, byte[] search, int fromIndex) {
		if(org==null) {
			org = new byte[0];
		}
		if(search==null) {
			search = new byte[0];
		}
		KMPMatcher kmpMatcher = new KMPMatcher();
		kmpMatcher.computeFailure4Byte(search);
		return kmpMatcher.lastIndexOf(org, fromIndex);
	}
	
	/**
	   * 将16进制字符串转为转换成字符串
	 * @param source  待转换字符串
	 * @return        转换后的字符串
	 * 2018年11月28日
	 * @author MBG
	 */
	public static byte[] hex2Bytes(String source) {
		if(source==null) {
			source = "";
		}
		byte[] sourceBytes = new byte[source.length() / 2];
		for (int i = 0; i < sourceBytes.length; i++) {
			sourceBytes[i] = (byte) Integer.parseInt(source.substring(i * 2, i * 2 + 2), 16);
		}
		return sourceBytes;
	}
	
	/**
	   * 将字节数组转换为16进制字符串
	 * @param srcBytes  待转换数组
	 * @return          转换后的数组
	 * 2018年11月28日
	 * @author MBG
	 */
	public static String byte2Hex(byte[] srcBytes) {
		if(srcBytes==null) {
			srcBytes = new byte[0];
		}
		StringBuilder hexRetSB = new StringBuilder();
		for (byte b : srcBytes) {
			String hexString = Integer.toHexString(0x00ff & b);
			hexRetSB.append(hexString.length() == 1 ? 0 : "").append(hexString);
		}
		return hexRetSB.toString();
	}
}
